package com.bdf.congcache.core.control;

import com.bdf.congcache.access.exception.CacheException;
import com.bdf.congcache.core.CacheConstants;
import com.bdf.congcache.core.ContextCacheAttributes;
import com.bdf.congcache.core.ElementAttributes;
import com.bdf.congcache.core.control.event.ElementEventQueue;
import com.bdf.congcache.core.control.event.IElementEventQueue;
import com.bdf.congcache.core.model.*;
import com.bdf.congcache.core.status.CacheStats;
import com.bdf.congcache.core.status.ICacheStats;
import com.bdf.congcache.kits.KitCache;
import com.bdf.congcache.kits.KitCacheAttributes;
import com.bdf.congcache.kits.KitCacheFactory;
import com.bdf.congcache.utils.config.OptionConverter;
import com.bdf.congcache.utils.threadpool.CacheKitThreadFactory;
import com.bdf.congcache.utils.threadpool.CongCacheThreadFactory;
import com.bdf.congcache.utils.threadpool.ThreadPoolManager;
import com.bdf.congcache.xml.XmlParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;


/**
 * @Author 田培融
 * @Description 缓存上下文件管理器
 * @Date 14:43 2019/7/10
 * @Param 
 * @return 
 **/
public class ContextCacheManager implements IContextCacheManager, IProvideScheduler {

    private static final Log log = LogFactory.getLog(ContextCacheManager.class);
    // 事件队列（内部包含执行器）
    private IElementEventQueue elementEventQueue;

    // 可以跑定时任务的线程池JUC包中的接口
    //用于定时执行压缩 内存
    private ScheduledExecutorService scheduledExecutor;

    private final LinkedBlockingDeque<IShutdownObserver> shutdownObservers = new LinkedBlockingDeque<IShutdownObserver>();


    // 保存缓存对象
    private final ConcurrentMap<String, ICache<?, ?>> caches = new ConcurrentHashMap<String, ICache<?, ?>>();

    private final ConcurrentMap<String, KitCacheAttributes> kitAttributeRegistry = new ConcurrentHashMap<String, KitCacheAttributes>();

    private final ConcurrentMap<String, KitCache<?, ?>> kitCaches = new ConcurrentHashMap<String, KitCache<?, ?>>();

//  重入锁，保证生成缓存上下文对象唯一
    private final ReentrantLock cacheLock = new ReentrantLock();

    //项目是否被初始化
    private boolean isInitialized = false;

    // 包含自己
    private static ContextCacheManager instance;

    // 释放资源的钩子
    private ShutdownHook shutdownHook;

    // 是否使用系统默认的配置文件
    private static final boolean DEFAULT_USE_SYSTEM_PROPERTIES = true;

    // 是否更新配置文件
    private static final boolean DEFAULT_FORCE_RECONFIGURATION = false;

    // 配置文件是否加载
    private boolean isConfigured = false;

    private Properties configurationProperties;

    private String defaultKitValues;

    private static final String DEFAULT_REGION = "CongCache.default";

    private IContextCacheAttributes defaultCacheAttr = new ContextCacheAttributes();

    private IElementAttributes defaultElementAttr = new ElementAttributes();

    // 缓存组件工厂
    private final ConcurrentMap<String, KitCacheFactory> kitFactoryRegistry = new ConcurrentHashMap<String, KitCacheFactory>();

    private final AtomicInteger clients = new AtomicInteger(0);


    public boolean isInitialized() {
        return isInitialized;
    }

    // static + synchronized 是类级锁 第二次保证只有一个实例被生成
    public static synchronized ContextCacheManager getInstance() throws CacheException
    {
        return getInstance(CacheConstants.DEFAULT_CONFIG);
    }

    // 真正生成缓存上下文件管理器的方法
    public static synchronized ContextCacheManager getInstance(String propsFilename) throws CacheException
    {
        if (instance == null)
        {
            if (log.isInfoEnabled())
            {
                log.info("Instance is null, creating with config [" + propsFilename + "]");
            }

            // 创建缓存上下文管理器
            instance = createInstance();
        }

        // 初始化管理
        if (!instance.isInitialized())
        {
            instance.initialize();
        }


        if (!instance.isConfigured())
        {
            instance.configure(propsFilename);
        }

        instance.clients.incrementAndGet();

        return instance;
    }

    /**
     * @Author 田培融
     * @Description 缓存管理器初始化
     * @Date 18:56 2019/7/19
     **/
    protected void initialize()
    {
        if (!isInitialized)
        {
            //初始化释放的钩子
            this.shutdownHook = new ShutdownHook();
            try
            {
                // 关jvm 关闭的时候执行钩子释放内存
                Runtime.getRuntime().addShutdownHook(shutdownHook);
            }
            catch (AccessControlException e)
            {
                log.error("Could not register shutdown hook.", e);
            }

            //  配置线程池
            this.scheduledExecutor = Executors.newScheduledThreadPool(4,
                    new CacheKitThreadFactory("CongCache-Scheduler-", Thread.MIN_PRIORITY));

            //  配置事件
            this.elementEventQueue = new ElementEventQueue();

            isInitialized = true;
        }
    }

    /**
     * @Author 田培融
     * @Description 1. 创建关闭时清理内存的钩子  2.配置线程池  3.配置事件队列 4.将是否已经初始化设置为true
     * @Date 17:03 2019/7/11
     **/
    protected void initialized(){
        if (!isInitialized){
            //初始化释放的钩子
            this.shutdownHook = new ShutdownHook();

            try {
                // 关jvm 关闭的时候执行钩子释放内存
                Runtime.getRuntime().addShutdownHook(shutdownHook);
            }catch (AccessControlException e){
                log.error("Could not register shutdown hook.", e);
            }

            // 配置线程池
            this.scheduledExecutor = Executors.newScheduledThreadPool(4,
                    new CongCacheThreadFactory("CongCache-Scheduler-",Thread.MIN_PRIORITY));

            // 配置事件队列
            this.elementEventQueue = new ElementEventQueue();

            isInitialized = true;
        }
    }


    //TODO 关闭服务还没有写完！！！！
    public void shutDown(){
        synchronized (ContextCacheManager.class){
            this.elementEventQueue.dispose();
            //TODO  shutdownNow 和shutdown哪个方法好呢？
            this.scheduledExecutor.shutdownNow();
            // 关闭所有连接池
            ThreadPoolManager.dispose();
            // 关闭服务观察者
            IShutdownObserver observer = null;
            while ((observer = shutdownObservers.poll())!=null){
                observer.shutdown();
            }

        }
    }


    // 加载默认的配置文件
    public void configure(Properties props)
    {
        configure(props, DEFAULT_USE_SYSTEM_PROPERTIES);
    }

    public void configure(Properties props, boolean useSystemProperties)
    {
        configure(props, useSystemProperties, DEFAULT_FORCE_RECONFIGURATION);
    }

    public synchronized void configure(Properties props, boolean useSystemProperties, boolean forceReconfiguration)
    {
        if (props == null)
        {
            log.error("No properties found.");
            return;
        }

        // 已经加载了
        if (isConfigured)
        {
            // 不在更新配置文件
            if (!forceReconfiguration)
            {

                return;
            }
            else
            {
                //TODO 更新配置文件
            }
        }
        if (useSystemProperties)
        {
            ContextCacheConfigurator.overrideWithSystemProperties(props);
        }

        doConfigure(props);
    }

    // 自定义配置文件
    public void configure(String xmlFile) throws CacheException
    {
        log.info("Create cache manager from config file: " + xmlFile);

        Properties props = new Properties();

        props = XmlParser.getPropertiesFromXml(xmlFile);
        configure(props);
    }

    /**
     * @Author 田培融
     * @Description 将赋值的properties 添加到管理器中的成员变量
     *   加载线程池管理器
     * @Date 8:32 2019/7/12
     **/
    private void doConfigure(Properties properties)
    {
        this.configurationProperties = properties;

        // 初始化线程程管理器
        ThreadPoolManager.setProps(properties);
        ThreadPoolManager poolMgr = ThreadPoolManager.getInstance();
        if (log.isDebugEnabled())
        {
            log.debug("ThreadPoolManager = " + poolMgr);
        }

        ContextCacheConfigurator configurator = newConfigurator();

        long start = System.currentTimeMillis();

        // 获取默认的kitValues  两个组件
        this.defaultKitValues = OptionConverter.findAndSubst(ContextCacheManager.DEFAULT_REGION, properties);

        log.info("Set default kits to " + defaultKitValues);

        // 获取缓存上下文的属性
        this.defaultCacheAttr = configurator.parseContextCacheAttributes(properties, "", new ContextCacheAttributes(),
                DEFAULT_REGION);

        log.info("Set defaultContextCacheAttributes to " + defaultCacheAttr);

        // 缓存元素的属性
        this.defaultElementAttr = configurator.parseElementAttributes(properties, "", new ElementAttributes(),
                DEFAULT_REGION);

        log.info("Set defaultElementAttributes to " + defaultElementAttr);


        configurator.parseSystemCaches(properties, this);

        // 加载配置文件时使用  生成缓存
        configurator.parseCaches(properties, this);

        long end = System.currentTimeMillis();
        if (log.isInfoEnabled())
        {
            log.info("Finish configuration in " + (end - start) + " ms.");
        }

        isConfigured = true;
    }




    // 生成缓存上下文管理器，并记录初化次数
    public static synchronized ContextCacheManager getUnconfiguredInstance(){
        if (instance == null){
            if (log.isDebugEnabled()){
                log.info("Instance is null,returning unconfigured instance");
            }
            // 创建 缓存管理器
            instance=createInstance();
        }

        if (!instance.isInitialized()){
            // 初始化
            instance.initialized();
        }
        // 记录缓存管理器初始化几次
        instance.clients.incrementAndGet();
        return instance;
    }

    protected ContextCacheManager()
    {

    }


    public void freeCache(String name, boolean fromRemote)
    {
        ContextCache<?, ?> cache = (ContextCache<?, ?>) caches.remove(name);

        if (cache != null)
        {
            cache.dispose(fromRemote);
        }
    }


    protected static ContextCacheManager createInstance()
    {
        return new ContextCacheManager();
    }

    protected ContextCacheConfigurator newConfigurator()
    {
        return new ContextCacheConfigurator();
    }




    class ShutdownHook extends Thread
    {
        @Override
        public void run()
        {
            // 如果初始化完成 执行下面的语句
            if (isInitialized())
            {
                log.info("Shut down CacheKit.");
                shutDown();
            }
        }
    }


    /**
     * @description: 通过缓存名称获取缓存上下文对象
     * @author: 田培融
     * @date: 2019/8/6 14:54
      * @param cacheName
     * @return: com.bdf.congcache.core.control.ContextCache<K,V>
     */
    @Override
    public <K, V> ContextCache<K, V> getCache(String cacheName)
    {
        // 获取缓存上下文配置文件
        return getCache(cacheName, getDefaultCacheAttributes());
    }

    public <K, V> ContextCache<K, V> getCache(String cacheName, IContextCacheAttributes cattr)
    {
        cattr.setCacheName(cacheName);
        return getCache(cattr, getDefaultElementAttributes());
    }
    public <K, V> ContextCache<K, V> getCache(String cacheName, IContextCacheAttributes cattr, IElementAttributes attr)
    {
        cattr.setCacheName(cacheName);
        return getCache(cattr, attr);
    }
    public <K, V> ContextCache<K, V> getCache(IContextCacheAttributes cattr)
    {
        return getCache(cattr, getDefaultElementAttributes());
    }

    @SuppressWarnings("unchecked")
    public <K, V> ContextCache<K, V> getCache(IContextCacheAttributes cattr, IElementAttributes attr)
    {
        ContextCache<K, V> cache;

        if (log.isDebugEnabled())
        {
            log.debug("attr = " + attr);
        }

//      先在concurrentHashMap中获取缓存上下文对象，如果不存在则生成
        cache = (ContextCache<K, V>) caches.get(cattr.getCacheName());

        if (cache == null)
        {
            cacheLock.lock();

            try
            {
                cache = (ContextCache<K, V>) caches.get(cattr.getCacheName());

                if (cache == null)
                {
                    ContextCacheConfigurator configurator = newConfigurator();


                    cache = configurator.parseCache(this.getConfigurationProperties(), this, cattr.getCacheName(),
                            this.defaultKitValues, cattr);

                    caches.put(cattr.getCacheName(), cache);
                }
            }
            finally
            {
                cacheLock.unlock();
            }
        }

        return cache;
    }


    @Override
    public Properties getConfigurationProperties()
    {
        return configurationProperties;
    }


    // 获取缓存组件
    @Override
    @SuppressWarnings("unchecked")
    public <K, V> KitCache<K, V> getKitCache(String kitName, String cacheName)
    {
        String key = String.format("Cong.%s.cacheName.%s", kitName, cacheName);
        return (KitCache<K, V>) kitCaches.get(key);
    }

    @Override
    public ScheduledExecutorService getScheduledExecutorService()
    {
        return scheduledExecutor;
    }

    @Override
    public void registerShutdownObserver(IShutdownObserver observer)
    {
        if (!shutdownObservers.contains(observer))
        {
            shutdownObservers.push(observer);
        }
        else
        {
            log.warn("Shutdown observer added twice " + observer);
        }
    }

    @Override
    public void deregisterShutdownObserver(IShutdownObserver observer)
    {
        shutdownObservers.remove(observer);
    }


    /**
     * @Author 田培融
     * @Description  获取状态
     * @Date 14:17 2019/7/18
     **/
    @Override
    public String getStats()
    {
        ICacheStats[] stats = getStatistics();
        if (stats == null)
        {
            return "NONE";
        }

        StringBuilder buf = new StringBuilder();
        int statsLen = stats.length;
        for (int i = 0; i < statsLen; i++)
        {
            buf.append("\n---------------------------\n");
            buf.append(stats[i]);
        }
        return buf.toString();
    }

    public ICacheStats[] getStatistics()
    {
        ArrayList<ICacheStats> cacheStats = new ArrayList<ICacheStats>();
        for (ICache<?, ?> c : caches.values())
        {
            ContextCache<?, ?> cache = (ContextCache<?, ?>) c;
            if (cache != null)
            {
                cacheStats.add(cache.getStatistics());
            }
        }
        ICacheStats[] stats = cacheStats.toArray(new CacheStats[0]);
        return stats;
    }

    public IElementAttributes getDefaultElementAttributes()
    {
        return this.defaultElementAttr.clone();
    }

    public IContextCacheAttributes getDefaultCacheAttributes()
    {
        return this.defaultCacheAttr.clone();
    }

    public IElementEventQueue getElementEventQueue()
    {
        return elementEventQueue;
    }

    public KitCacheFactory registryFacGet(String name)
    {
        return kitFactoryRegistry.get(name);
    }

    public void addCache(String cacheName, ICache<?, ?> cache)
    {
        caches.put(cacheName, cache);
    }

    public void registryFacPut(KitCacheFactory kitFac)
    {
        kitFactoryRegistry.put(kitFac.getName(), kitFac);
    }

    public KitCacheAttributes registryAttrGet(String name)
    {
        return kitAttributeRegistry.get(name);
    }

    public void registryAttrPut(KitCacheAttributes kitAttr)
    {
        kitAttributeRegistry.put(kitAttr.getName(), kitAttr);
    }

    public void addKitCache(String kitName, String cacheName, KitCache<?, ?> cache)
    {
        String key = String.format("kit.%s.cacheName.%s", kitName, cacheName);
        kitCaches.put(key, cache);
    }

    public boolean isConfigured()
    {
        return isConfigured;
    }

}
